﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;
using Microsoft.Practices.ObjectBuilder2;

using SharedGenomics.Workbench.Core;

namespace SharedGenomics.Workbench.Tests
{
    [TestClass]
    public class UnityTests
    {
        [TestMethod]
        public void InterceptionTest()
        {
            IUnityContainer container = new UnityContainer();
            container.RegisterType<ValueSource>();
            container.AddNewExtension<Interception>();
            container.Configure<Interception>().AddPolicy("MatchingRule").AddMatchingRule(new MatchingRule()).AddCallHandler(new InterceptingCallHandler());
            container.Configure<Interception>().SetDefaultInterceptorFor<ValueSource>(new VirtualMethodInterceptor());

            var source = container.Resolve<ValueSource>();
            var instance = source.GetInstance<ProxiedType>();
            Assert.IsInstanceOfType(instance, typeof(ChildProxiedType));
        }

        [TestMethod]
        public void ExtensionTest()
        {
            IUnityContainer container = new UnityContainer();
            container.RegisterType<IEventBroker, EventBroker>(new ContainerControlledLifetimeManager());
            container.AddNewExtension<EventBrokerExtension>();

            EventSubscriber sub = container.Resolve<EventSubscriber>();
            IEventBroker broker = container.Resolve<IEventBroker>();
            broker.Publish("abc");

            Assert.AreEqual("abc", sub.LastReceived);
        }
    }

    public class InterceptingCallHandler : ICallHandler
    {
        #region ICallHandler Members

        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
        {
            var methodReturn = getNext().Invoke(input, getNext);
            object returnValue = methodReturn.ReturnValue;

            if (returnValue is ProxiedType)
                methodReturn.ReturnValue = new ChildProxiedType();

            return methodReturn;
        }

        public int Order { get; set; }

        #endregion
    }

    public class MatchingRule : IMatchingRule
    {
        public bool Matches(System.Reflection.MethodBase member)
        {
            return member.Name.Equals("GetInstance", StringComparison.InvariantCultureIgnoreCase);
        }
    }

    public class ValueSource
    {
        public virtual int GetValue()
        {
            return 2;
        }

        public virtual T GetInstance<T>() where T : class, new()
        {
            return new T();
        }
    }

    public class EventBrokerExtension : UnityContainerExtension
    {
        protected override void Initialize()
        {
            this.Context.Locator.Add(typeof(IEventBroker), this.Container.Resolve<IEventBroker>());
            this.Context.Strategies.AddNew<EventBrokerRegistrationStrategy>(Microsoft.Practices.Unity.ObjectBuilder.UnityBuildStage.Creation);
        }
    }

    public class EventBrokerRegistrationStrategy : BuilderStrategy
    {
        public override void PreBuildUp(IBuilderContext context)
        {
            var eventBroker = context.Locator.Get<IEventBroker>();
            Type typeToBuild = BuildKey.GetType(context.BuildKey);
            Type ifaceType = typeToBuild.GetInterfaces()[0];

            eventBroker.Subscribe(ifaceType.GetGenericArguments()[0], context.Existing);
        }
    }

    public class EventSubscriber : IEventSubscriber<string>
    {
        public void Receive(string message)
        {
            this.LastReceived = message;
        }

        public string LastReceived { get; private set; }
    }

    public class ProxiedType { }
    public class ChildProxiedType : ProxiedType { }
    public class NonProxiedType { }
}
